#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>

MODULE_LICENSE("GPL");

typedef struct
{
    int m_num;
    const char* m_name;
}led_desc_t;

led_desc_t ledDescs[] = 
{
    {74,"led1"},
    {90,"led2"},
    {72,"led3"}
};

static dev_t ledDevNum;
static struct cdev* ledCdev;

int led_open(struct inode *inode, struct file *filp)
{
    printk("/dev/led1 try open\n");
    return 0;
}
long led_ioctl(struct file *filp, unsigned int request , unsigned long arg)
{
    int ledIndex,ledState,ledReadState;
    copy_from_user(&ledIndex, (void*)arg, _IOC_SIZE(request));
    ledState = _IOC_NR(request);
    if(ledState == 0 || ledState == 1){
        gpio_set_value(ledDescs[ledIndex - 1].m_num, ledState);
    }else if(ledState == 2){
        ledReadState = gpio_get_value(ledDescs[ledIndex - 1].m_num);
        copy_to_user((void*)arg,&ledReadState,_IOC_SIZE(request));
    }else{
        printk("invalid argment!\n");
        return -EINVAL;
    }
    return 0;
}

struct file_operations ledFops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .unlocked_ioctl = led_ioctl
};
static int __init led_drv_init(void)
{
   int i;

    alloc_chrdev_region(&ledDevNum,0,1,"led1");
    ledCdev = cdev_alloc();
    cdev_init(ledCdev,&ledFops);
    cdev_add(ledCdev,ledDevNum,1);
 
    for(i=0;i<ARRAY_SIZE(ledDescs);i++)
    {
        gpio_request(ledDescs[i].m_num,ledDescs[i].m_name);
        gpio_direction_output(ledDescs[i].m_num,0);
    }
    return 0;
}

static void __exit led_drv_exit(void)
{
    int i;
    for(i = 0; i < ARRAY_SIZE(ledDescs); i++)
    {
        gpio_free(ledDescs[i].m_num);
    }
    cdev_del(ledCdev);
    unregister_chrdev_region(ledDevNum,1);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
